﻿#define _CRT_SECURE_NO_WARNINGS 1

#include "SList.h"





void SLTPrint(SLTNode* phead)
{
	//////////////////////////////////////////////////////////////////////
	/////////////////////////////////////////////////////////////////////
	////////////////////////这里不需要用断言是因为链表传入的地址是一个节点的地址，它一定是有意义的，而顺序表中传入的地址是结构体
	///////////////////////的地址，要想访问结构体的成员变量必须保证该地址不是空地址
	////////////////////////////////////////////////////////////////////
	SLTNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;//////////////////////////////////////////////////
		/////////////////////////////////////////////////////////////////这里是访问结构体成员变量，该变量是结构体指针也是下一节点
		////////////////////////////////////////////////////////////////的指针，next是该指针的名字
	}
	printf("NULL\n");
}






SLTNode* BuySLTNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}






///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////不为空链表的尾插的本质即链表的本质是：原结点中要存放尾结点的地址，即原结点的地址指向的是一个结构体，
//////////////////////而结构体中要存放下一结点的地址，这样才能将一个新结点连接到原来的结点上
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);//////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////pphead时结点的地址，一定不能为空，凡是不能为空的指针都需要进行断言，可以快速找到错误位置，只要用到
	//////////////////////////////////////////////////////断言就说明断言内的指针为空指针
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		printf("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	//找尾
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}

}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////改变int的值传参时传入int的地址      改变int*的值传参时传入int**的地址
///////////////判断是否使用二级指针，就要判断是否要改变指针的值







void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuySLTNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}








void SLTPopBack(SLTNode** pphead)
{
	assert(pphead);
	//暴力检查
	assert(*pphead);

	//温柔检查
	//if (*pphead == NULL)
		//return;

	//只有一个结点    有多个结点

	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;//////////////////////////////////////////
	}//////////////////////////////////////////////////要想改变指针的值就要用二级指针

	else
	{

		//找尾---------找到最后一个结点，然后先将该节点的空间释放，再将前一节点next置空
	/*	SLTNode* prev = NULL;
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;

		}
		free(tail);
		tail = NULL;
		prev->next = NULL;
		*/

		SLTNode* tail = *pphead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;

	}

}





void SLTPopFront(SLTNode** pphead)
{
	assert(pphead);
	//暴力检查
	assert(*pphead);

	//温柔检查
	//if (*pphead == NULL)
		//return;

	SLTNode* first = *pphead;
	*pphead = first->next;
	free(first);
	first = NULL;


}







SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}





            
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pos);
	assert(pphead);
	if (pos == *pphead)
	{
		SLTPushFront(pphead, x);
	}
	else
	{
		//找到pos的前一个位置
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SLTNode* newnode = BuySLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}





void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pos);/////////////////////////////////////////////////////////
	////////////////////////////////////////pos断言已经起到断言*pphead的作用，因为pos就是链表中的某一个结点
	assert(pphead);
	if (*pphead == pos)
	{
		SLTPopFront(pphead);

	}
	else
	{
		// 找到pos的前一个位置
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		prev->next = pos->next;
		free(pos);
		//pos = NULL;///////////////////////这里无法达到置空的效果，因为要想改变pos的值就要用二级指针接收pos的地址，利用二级指针改变
		/////////////////////////////////////////////////可以外部置空，也能将pos的值置空
	}

}




void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = BuySLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;//////////////////////////////////////
	///////////////////////////////注意：两行代码不能写反了
}





void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);
	assert(pos->next);

	//SLTNode* del = pos->next;///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// /////////////////////////////////////这里要保存pos->next的值，因为下一行代码执行后会将该值丢失，那样就无法将要删除的结点的空间释放以及将该节点的指针的值置空
	//pos->next = pos->next->next;
	//free(del);///////////////////////////////////////////////////////////////////////////////////////////////////////////
	//del = NULL;///////////////////////这里是在改变指针的值，但是不同的是这里的指针不是外界传来的，可以直接在该函数中自由使用并置空


	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;

}




//void SLTDestroy(SLTNode* phead)
//{
//	SLTNode* cur = phead;
//	while (cur)
//	{
//		SLTNode* tmp = cur->next;
//		free(cur);
//		cur = tmp;
//	}
//}




void SLTDestroy(SLTNode** pphead)
{
	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* tmp = cur->next;
		free(cur);
		cur = tmp;
	}
	*pphead = NULL;
}









													/*
													1. free的作用：释放空间

													- 核心功能： free(ptr) 用于释放 ptr 指向的动态分配内存（如 malloc / calloc / realloc 分配的空间）。
													​
													- 操作对象：仅针对内存空间，不改变指针变量本身的值（指针仍指向原来的地址）。
													​
													- 关键注意：释放后若继续使用该指针（“野指针”），会导致未定义行为（如程序崩溃、数据错误）。
 
													2. 指针置空（ ptr = NULL ）的作用：避免野指针
 
													- 核心功能：将指针变量的值设为 NULL （空指针），使其不再指向任何有效内存地址。
													​
													- 操作对象：针对指针变量本身，不影响已分配的内存（若未 free ，内存仍被占用，导致内存泄漏）。
													​
													- 关键价值：防止后续误操作（如判断 if (ptr != NULL) 可避免访问空指针）。

													通常是先释放空间在置空

													*/